package br.pucrio.inf.tamdera.prolog;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;


import br.pucrio.inf.tamdera.Constants;
import br.pucrio.inf.tamdera.QueryReportBean;
import br.pucrio.inf.tamdera.RulesSpecification;
import br.pucrio.inf.tamdera.prolog.consistency.AntiErosionConsistencyChecker;
import br.pucrio.inf.tamdera.prolog.qbeans.AntiErosionRulesConsistencyQueryBean;
import br.pucrio.inf.tamdera.prolog.qbeans.PrologQueryBean;
import br.pucrio.inf.tamdera.prolog.utils.PrologIntegrationConstants;
import br.pucrio.inf.tamdera.utils.ConverterPrologTamderaUtils;
import br.pucrio.inf.tamdera.utils.FileUtils;
import br.pucrio.inf.tamdera.utils.Pair;


public class PrologRepresentationManager {

	private final String headerConceptMappingFile = "% ConceptMapping generated By Tamdera "  
			+ PrologIntegrationConstants.LINE_SEPARATOR + PrologIntegrationConstants.LINE_SEPARATOR ;

	private final String headerMeasureMappingFile = "% Metric Mapping generated By Tamdera "  
			+ PrologIntegrationConstants.LINE_SEPARATOR + PrologIntegrationConstants.LINE_SEPARATOR ;
	
	private final String headerAntiErosionDesignRuleFile = "% Anti-ErosionDesign Rules Logical Facts " +
			"generated By Tamdera " + PrologIntegrationConstants.LINE_SEPARATOR + 
			"% FACTS- dependency(<type>, <conceptA>, <conceptB>)." + PrologIntegrationConstants.LINE_SEPARATOR 
			+ PrologIntegrationConstants.LINE_SEPARATOR ;
	

	/* the file in pc containg the prolog representation of the system which is being annalyzed*/
	private String projectPrologFilePath;

	public PrologRepresentationManager(String projectProloFilePath) {
		super();
		this.projectPrologFilePath = projectProloFilePath;
	}

	public void generateMappingPrologFile ( HashMap<String, List<String>> mapping ) throws IOException
	{
		StringBuffer mappingConceptText = new StringBuffer();

		mappingConceptText.append( headerConceptMappingFile );

		for ( String concept : mapping.keySet() )
		{
			List<String> classes = mapping.get( concept );

			for ( String classNameWithPackage : classes )
			{

				Pair< String,String > pairPackageNameClassInProlog = ConverterPrologTamderaUtils.getPrologClassNameFromTamderaClassName ( classNameWithPackage );

				String conceptMapperFact = String.format( PrologIntegrationConstants.CONCEPT_MAPPING_FACT
						, concept, pairPackageNameClassInProlog.getFirst(), pairPackageNameClassInProlog.getSecond() );

				mappingConceptText.append( conceptMapperFact + PrologIntegrationConstants.LINE_SEPARATOR );
			}
			mappingConceptText.append( PrologIntegrationConstants.LINE_SEPARATOR );
		}

		/*generating outputFile*/
		FileUtils.generateFile ( PrologIntegrationConstants.mappingPrologFile, mappingConceptText );
	}

	public List<QueryReportBean> checkDependencyRules( RulesSpecification spec ) 
	{
		PrologAntiErosionQueryVisitor visitor = new PrologAntiErosionQueryVisitor();
		visitor.generateQuery( spec );

		List<PrologQueryBean> prologQueries = visitor.getQueries();
		List<QueryReportBean> reports = excuteQueries( prologQueries );

		return reports;
	}

	private List<QueryReportBean> excuteQueries(List<PrologQueryBean> prologQueries) 
	{
		List<QueryReportBean> reports = new ArrayList<QueryReportBean>();
		for (PrologQueryBean query : prologQueries )
		{
			QueryReportBean report = PrologInterface.executeQuery ( query, projectPrologFilePath );
			reports.add( report );
		}
		return reports;
	}

	public List<String> getSubClassesOfClasses(List<String> mappedParentClasses) {
		return PrologInterface.getSubClassesOfClasses( mappedParentClasses , projectPrologFilePath );
	}

	public List<String> extractAllClassesFromPrologProjectFile() 
	{
		return PrologInterface.extractAllClassesFromPrologProjectFile ( projectPrologFilePath );
	}

	public void generateMeasureMappingPrologFile(
			Hashtable<String, Hashtable<String, Double>> measures) throws IOException 
	{
		StringBuffer mappingText = new StringBuffer();

		mappingText.append( headerMeasureMappingFile );

		for ( String sourceClassName : measures.keySet() )
		{
			
			//TODO verificar se comeca com letra maiusucla senao der continue
			
			
			Pair< String,String > pairPackageNameClassInProlog = 
					ConverterPrologTamderaUtils.getPrologClassNameFromTamderaClassName ( sourceClassName );
			
			Hashtable<String, Double> values = measures.get(sourceClassName);
			
			List<String> metrics = Constants.METRICS;
			
			for (String metric : metrics)
			{
				final Double value = values.get( metric );
				final String metricName = metric.toLowerCase(); 
				
				final String mapping = String.format(PrologIntegrationConstants.METRIC_VALUE_MAPPING, metricName, 
						pairPackageNameClassInProlog.getFirst(), pairPackageNameClassInProlog.getSecond(), value);
				
				mappingText.append( mapping + PrologIntegrationConstants.LINE_SEPARATOR );
			}
		}

		/*generating outputFile*/
		FileUtils.generateFile ( PrologIntegrationConstants.propertyMappingPrologFile, mappingText );

	}

	public List<QueryReportBean> checkDriftRules(RulesSpecification spec) {
		PrologAntiDriftQueryVisitor visitor = new PrologAntiDriftQueryVisitor();
		visitor.generateQuery( spec );

		List<PrologQueryBean> prologQueries = visitor.getQueries();
		List<QueryReportBean> reports = excuteQueries( prologQueries );

		return reports;
	}

	public void generateDesignRulesLogicalFacts(RulesSpecification spec) throws IOException 
	{
		
		PrologAntiErosionDesignRuleExtractorVisitor extractor = new PrologAntiErosionDesignRuleExtractorVisitor();
		extractor.extractDesignRules( spec );
		List<String> logicalFacts = extractor.getDesignRuleLogicalFacts();
		
		final StringBuffer text = new StringBuffer();
		text.append( headerAntiErosionDesignRuleFile );
		
		for ( String str : logicalFacts  )
		{
			text.append(str + PrologIntegrationConstants.LINE_SEPARATOR );
		}
		
		FileUtils.generateFile( PrologIntegrationConstants.antiErosionDesignRulePrologFile , text);
	}

	public List<QueryReportBean> checkConsistencyOfAntiErosionRules() 
	{
		final String checkOfForbidAllowDependencyIncosistency = AntiErosionConsistencyChecker.getQueryForCheckingForbidAllowAntiErosionRules();
		final String checkOfOnlyCanIncosistency = AntiErosionConsistencyChecker.getQueryForCheckingOnlyCanAntiErosionRules();
		final String checkOfCyclicDependencyIncosistency = AntiErosionConsistencyChecker.getQueryForCheckingCyclicAntiErosionRules();
		
		
		final String message1 = "Checking consistency between forbidden/allowing dependencies";
		final PrologQueryBean forbidAllowInconstBean = new AntiErosionRulesConsistencyQueryBean(checkOfForbidAllowDependencyIncosistency, message1);
		
		final String message2 = "Checking consistency between onlycan dependencies";
		final PrologQueryBean onlyCanInconstBean = new AntiErosionRulesConsistencyQueryBean(checkOfOnlyCanIncosistency, message2);
		
		final String message3 = "Checking cyclic dependencies";
		final PrologQueryBean cyclicDependencyInconstBean = new AntiErosionRulesConsistencyQueryBean(checkOfCyclicDependencyIncosistency, message3);
		
		final List<PrologQueryBean> queries = new ArrayList<PrologQueryBean>();
		
		queries.add(forbidAllowInconstBean);
		queries.add(onlyCanInconstBean);
		queries.add(cyclicDependencyInconstBean);
		
		List<QueryReportBean> reports = excuteQueries(queries);
		
		return reports;
	}
}
